
/**
 * * es6新特性
 * * 1. 块级作用域
 * * 2. let/const
 * * 3. 解构数组、结构对象
 * * 4. 模板字符串
 * * 5. 函数参数默认值
 * * 6. 三点操作符:函数剩余参数、展开数组、展开对象
 * * 7. 上下文增强：摆脱this的影响、简化函数写法的箭头函数
 * * 8. 字面量增强：对象（同名简写、函数简写、计算属性名）
 * * 9. 对象方法新增：assign()、is() 
 * * 10. 数据读写增强：proxy
 * * 11. 全新内置对象 Reflect(13、静态类（Math）) 
 * * 12. 类语法增强
 * * 13. 唯一标识数据类型：Symbol
 * 
 */
//! 1
{
  var foo = 'zp'
  // let foo = 'zp'
}
// console.log(foo)

//* 问题代码实例1
for (var i = 0; i < 3; i++) {
  for (var i = 0; i < 3; i++) {
    // console.log(i) //希望9次，只有3次。因为内层循环结束i为3，执行了3 * 1次。
  }
}

//* 问题代码实例2：循环添加事件（伪代码）
var elements = [{}, {}]
for (var i = 0; i < elements.length; i++) {
  elements[i].onclick = function () {
    console.log(i) //调用时，全局i已经变为3
  }

  // 之前的解决方案：闭包(借助函数作用域，消除全局作用域的影响)
  elements[i].onclick = (function (i) {
    return function (i) {
      console.log(i)
    }
  })(i)
}

//* for循环的两层作用域
for (let i = 0; i < 3; i++) {
  let i = 'foo'
  // console.log(i) //3个'foo'
}
// 代码解析： let i = 0; if (i < 3) { let i = 'foo' }

//! 解构
//* 1. 指定位置
const [, , aa] = [1, 2, 3]
console.log(aa)
//* 2. 提取剩余成员
const [, ...part] = [1, 2, 3]
console.log(part)//数组 [ 2, 3 ]
let { a, ...other } = { a: 1, b: 2, c: 3 }
console.log(other)//对象 { b: 2, c: 3 }
//* 3. 设置默认值
const [, , , d = 4] = [1, 2, 3]
// console.log(d) //4
let { dd = 4 } = { a: 1, b: 2, c: 3 }
console.log(dd) //4
//* 4.解构变量重命名(对象)
const { a: a1 } = { a: 1, b: 2, c: 3 }
console.log(a1)
//* 5. 函数剩余参数
function fn0 (...args) {
  console.log('args', args)//数组形式接受
}
fn0(1, 2)
//* 实际开发场景
const path = '/foo/zp/post'
const [, , name] = path.split('/')
console.log(name)

//! 4
//* 1.支持转义  
//* 2.支持多行字符串
//* 3.变量插值
//* 4.带标签的模板字符串（可以对字符串二次加工，甚至做一个小型的模板引擎）
//* 5.字符串扩展方法：starstWith/includes/endsWith 一组方法查找 前中后
const Person = 'lym'
const handler = (strings, Person) => {
  const domainMap = new Map([
    ['lym', '123.456']
  ])
  // console.log(strings)
  return domainMap.get(Person) + strings[1]
}
const domain = handler`${Person}/sjgl`
// console.log('domain', domain)


//! 5
function fn1 (a = 1, b) { //建议有默认值参数，放在最后面
  console.log(a, b)
}
// fn1()


//! 6
//*  1. 展开数组
/* let arr1 = [1, 2]
let arr2 = [3, 4]
let arr3 = [
  ...arr1,
  ...arr2
]
console.log('arr3', arr3) // arr3 [ 1, 2, 3, 4 ] */
//*  1. 展开对象
/* let obj1 = { a: 1 }
let obj2 = { b: 1 }
let obj3 = {
  ...obj1,
  ...obj2
}
console.log('obj3', obj3) // obj3 { a: 1, b: 1 } */

//* 数组元素重复  允许重复元素

let arr1 = [1, 2, 3]
let arr2 = [2, 3, 4]
let arr3 = [
  ...arr1,
  ...arr2
]
console.log('arr3', arr3) // arr3 [ 1, 2, 3, 2, 3, 4 ]
//* 对象元素重复  后面覆盖前面
let obj1 = { a: 1, c: 2 }
let obj2 = { b: 1, c: 3 }
let obj3 = {
  ...obj1,
  ...obj2
}
console.log('obj3', obj3) // obj3 { a: 1, c: 3, b: 1 }
//! 7
//* 1. 简化写法
const fn2 = a => a + 1
console.log(fn2(3)) //4
//* 2. 不改变this指向 (函数外面作用域是什么，里边就是什么)
const person = {
  name: 'tom',
  sayHi1 () {
    console.log(this)
  },
  sayHi2: () => {
    console.log(this)
  }
}
person.sayHi1() //person
person.sayHi2() //{} 

//! 9
//* 1. is()判断两个值是否相等
console.log('相等判断：', `
${0 == false} //true
${0 === false} //false
${+0 === -0} //true 区分不开正负0
${NaN === NaN} //false 之前认为NaN有无限制可能；现在认为就是特别的值
`)
console.log('is()判断：', `
${Object.is(0, false)} //false
${Object.is(+0, -0)} //false
${Object.is(NaN, NaN)} //true
`)

//! 10

const zp = {
  name: 'zp',
  age: '18'
}
const proxyZp = new Proxy(zp, {//操作读写对象
  get (target, property) {
    // Number.isInteger() //整数校验
  },
  set (target, property, value) {

  }
})
//* 对数组方法的更好监视 
const numbers = [1, 2, 3]
const proxyNum = new Proxy(numbers, {//操作读写对象
  set (target, property, value) {
    console.log(target, property, value)
    // target[property] = value
    return true
  }
})
proxyNum.push(4) //create
// numbers[1] //read
// numbers[1] ='aa' //update
// delete number[2] //delete
//! 11
const peter = {
  name: 'peter',
  age: '18'
}
// * Reflect成员方法就是proxy处理对象的默认实现
const proxypeter = new Proxy(peter, {//操作读写对象
  get (target, property) { //get方法的默认实现
    return Reflect.get(target, property)
  },
})
// * 操作对象前后对比

// delete
// delete peter.age
// Reflect.deleteProperty(peter, 'age')

console.log(`
// has
${'age' in peter}
${Reflect.has(peter, 'age')}
// keys

${Object.keys(peter)}
${Reflect.ownKeys(peter)}
`)

//! 12

class Man {
  constructor(name) {
    this.name = name //this指向实例对象
  }
  // 静态成员
  // 实例成员
}

// 继承
class Bob extends Man {
  constructor(name) {
    // 调用构造函数
    super(name)
    this.name = name
    this.age = 18
  }
}
// 访问实例属性


let setArr = [1, 2, 3, 3, 4, 4, 5]
// setArr = Array.from(new Set(setArr)) //法1
setArr = [...new Set(setArr)] //法2
console.log(setArr)
const myName = Symbol('myName')//参数可区分哪个symbol
const myObj = {
  [myName]: 'zp'
}
console.log(myObj[myName], myName) //对象属性名可以是string/symbol

const keys = ['a', 'b', 'c', 'd']
const symbolObj = {}
keys.forEach((item, index) => {
  const s = Symbol(item)
  Reflect.set(keys, index, s)
  Reflect.set(symbolObj, s, index)
})
console.log(symbolObj)
console.log(keys)

//! 13 symbol
console.log(Symbol('a') === Symbol('a')) //false
console.log(Symbol.for('b') === Symbol.for('b')) //true
// * 1. 内置属性
const symbolConstObj = {
  [Symbol.toStringTag]: 'myObj'
}
console.log(symbolConstObj.toString()) //[object myObj]
// 维护了数据指针，每调用next都往后移动一位。
Object.getOwnPropertyDescriptor